﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;

namespace Utility
{
    public class DicomHelper
    {
        string fileName = "";
        Dictionary<string, string> tags = new Dictionary<string, string>();//dicom文件中的标签
        BinaryReader dicomFile;//dicom文件流

        //文件元信息
        public Bitmap gdiImg;//转换后的gdi图像
        UInt32 fileHeadLen;//文件头长度
        long fileHeadOffset;//文件数据开始位置
        UInt32 pixDatalen;//像素数据长度
        long pixDataOffset = 0;//像素数据开始位置
        bool isLitteEndian = true;//是否小字节序（小端在前 、大端在前）
        bool isExplicitVR = true;//有无VR

        //像素信息
        int colors;//颜色数 RGB为3 黑白为1
        public int windowWith = 0, windowCenter = 0 / 2;//在未设定时 窗宽窗位为0
        int rows, cols;

        public string readAndShow()
        {
            string strMessage = "";

            if (fileName == string.Empty)
                return strMessage;

            dicomFile = new BinaryReader(File.OpenRead(fileName));

            //跳过128字节导言部分
            dicomFile.BaseStream.Seek(128, SeekOrigin.Begin);

            if (new string(dicomFile.ReadChars(4)) != "DICM")
            {
                throw new Exception("没有dicom标识头，文件格式错误");
            }

            tagRead();

            IDictionaryEnumerator enor = tags.GetEnumerator();
            while (enor.MoveNext())
            {
                if (enor.Key.ToString().Length > 9)
                {
                    strMessage += enor.Key.ToString() + "\r\n";
                    strMessage += enor.Value.ToString().Replace('\0', ' ');
                }
                else
                    strMessage += enor.Key.ToString() + enor.Value.ToString().Replace('\0', ' ') + "\r\n";
            }
            dicomFile.Close();

            return strMessage;
        }

        public DicomHelper(string _filename)
        {
            fileName = _filename;
        }

        public void saveAs(string filename)
        {
            switch (filename.Substring(filename.LastIndexOf('.')))
            {
                case ".jpg":
                    gdiImg.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg);
                    break;
                case ".bmp":
                    gdiImg.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);
                    break;
                case ".png":
                    gdiImg.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
                    break;
                default:
                    break;
            }
        }

        public Image getImg(byte[] data, int width, int height, int bitAloc, int dataLen)
        {
            MemoryStream ms = new MemoryStream(data);
            BinaryReader br = new BinaryReader(ms);

            int ww = 0, wl = 0;
            ww = 1 << bitAloc;
            wl = ww / 2;

            Bitmap gdiImg = new Bitmap(width, height);
            long reads = 0;
            for (int i = 0; i < gdiImg.Height; i++)
            {
                for (int j = 0; j < gdiImg.Width; j++)
                {
                    if (reads >= pixDatalen)
                        break;
                    byte[] pixData;

                    pixData = dicomFile.ReadBytes(dataLen / 8 * colors);
                    reads += pixData.Length;

                    Color c = Color.Empty;
                    if (colors == 1)
                    {
                        int grayGDI;

                        double gray;
                        if (bitAloc <= 8)
                            gray = (double)pixData[0];
                        else
                            gray = BitConverter.ToUInt16(pixData, 0);

                        //调窗代码，就这么几句而已
                        //1先确定窗口范围 2映射到8位灰度
                        int grayStart = (windowCenter - windowWith / 2);
                        int grayEnd = (windowCenter + windowWith / 2);

                        if (gray < grayStart)
                            grayGDI = 0;
                        else if (gray > grayEnd)
                            grayGDI = 255;
                        else
                        {
                            grayGDI = (int)((gray - grayStart) * 255 / windowWith);
                        }
                        if (grayGDI > 255)
                            grayGDI = 255;
                        else if (grayGDI < 0)
                            grayGDI = 0;
                        c = Color.FromArgb(grayGDI, grayGDI, grayGDI);
                    }
                    else if (colors == 3)
                    {
                        c = Color.FromArgb(pixData[0], pixData[1], pixData[2]);
                    }

                    gdiImg.SetPixel(j, i, c);
                }
            }

            //MessageBox.Show(string.Format("max{0} min{1}", max, min));
            dicomFile.Close();
            return gdiImg;
        }

        public bool getImg()//获取图像 在图像数据偏移量已经确定的情况下
        {
            if (fileName == string.Empty)
                return false;

            int dataLen, validLen, hibit;//数据长度 有效位
            int imgNum;//帧数
            int ww = 0, wl = 0;//窗宽窗位

            rows = int.Parse(tags["0028,0010"].Substring(5));
            cols = int.Parse(tags["0028,0011"].Substring(5));

            colors = int.Parse(tags["0028,0002"].Substring(5));
            dataLen = int.Parse(tags["0028,0100"].Substring(5));
            validLen = int.Parse(tags["0028,0101"].Substring(5));
            hibit = int.Parse(tags["0028,0102"].Substring(5));

            //读取预设窗宽窗位
            if (windowWith == 0 && windowCenter == 0)
            {
                //    Regex r = new Regex(@"([0-9]+)+");
                //    if (tags.ContainsKey("0028,1051"))
                //    {
                //        Match m = r.Match(tags["0028,1051"].Substring(5));

                //        if (m.Success)
                //            windowWith = int.Parse(m.Value);
                //        else
                //            windowWith = 1 << validLen;
                //    }
                //    else
                //    {
                //        windowWith = 1 << validLen;
                //    }

                //    if (tags.ContainsKey("0028,1050"))
                //    {
                //        Match m = r.Match(tags["0028,1050"].Substring(5));
                //        if (m.Success)
                //            windowCenter = int.Parse(m.Value);// + (1 << hibit) / 2窗位的偏移方式 神啊
                //        else
                //            windowCenter = windowWith / 2;
                //    }
                //    else
                //    {
                //        windowCenter = windowWith / 2;
                //    }
                //    //靠 窗宽窗位到底怎么弄啊 以什么为依据啊
                windowWith = 1 << validLen;
                windowCenter = windowWith / 2;
            }

            gdiImg = new Bitmap(cols, rows);

            BinaryReader dicomFile = new BinaryReader(File.OpenRead(fileName));

            dicomFile.BaseStream.Seek(pixDataOffset, SeekOrigin.Begin);

            long reads = 0;

            /*
            byte[] pixelData = dicomFile.ReadBytes((int)pixDatalen);
            ushort planar =(tags.ContainsKey("0028,0006")?ushort.Parse(tags["0028,0006"]):(ushort) 0);
            if (colors == 3)
            {
                gdiImg=(Helpers.TransformRGB(pixelData,  cols, rows, planar, ref colors));
            }
            else if (validLen == 16)
            {
                ww = 65536 + ww;// 默认窗宽65536，默认window level 是窗宽的一半
                wl = 32768 + wl;
                gdiImg=(Helpers.CreateImage16(pixelData, wl, ww, cols, rows));
            }
            else if (validLen == 12)
            {
                ww = 2 << validLen + ww;// 默认窗宽65536，默认window level 是窗宽的一半
                wl = (2 << validLen) / 2 + wl;
                gdiImg = (Helpers.CreateImage12(pixelData, wl, ww, cols, rows));
            }
            else if (validLen == 8)
            {
                ww = 256 + ww;
                wl = 128 + wl;
                gdiImg = (Helpers.CreateImage8(pixelData, wl, ww, cols, rows));
            }*/
            //double max = UInt16.MinValue, min = UInt16.MaxValue;
            //min = (2 * windowCenter - windowWith) / 2.0 + 0.5;
            //max = (2 * windowCenter + windowWith) / 2.0 + 0.5;

            for (int i = 0; i < gdiImg.Height; i++)
            {
                for (int j = 0; j < gdiImg.Width; j++)
                {
                    if (reads >= pixDatalen)
                        break;
                    byte[] pixData;

                    pixData = dicomFile.ReadBytes(dataLen / 8 * colors);
                    reads += pixData.Length;

                    Color c = Color.Empty;
                    if (colors == 1)
                    {
                        int grayGDI;

                        double gray;
                        if (validLen <= 8)
                            gray = (double)pixData[0];
                        else
                            gray = BitConverter.ToUInt16(pixData, 0);

                        //if (gray < min)
                        //    min = gray;
                        //if (gray > max)
                        //    max = gray;

                        //调窗代码，就这么几句而已
                        //1先确定窗口范围 2映射到8位灰度

                        //关于窗位标准的偏移问题 真让人蛋疼
                        //int _windowCenter = windowCenter + 1024;
                        //int grayStart = (_windowCenter - windowWith / 2);
                        //int grayEnd = (_windowCenter + windowWith / 2);
                        int grayStart = (windowCenter - windowWith / 2);
                        int grayEnd = (windowCenter + windowWith / 2);

                        if (gray < grayStart)
                            grayGDI = 0;
                        else if (gray > grayEnd)
                            grayGDI = 255;
                        else
                        {
                            grayGDI = (int)((gray - grayStart) * 255 / windowWith);
                        }
                        //grayGDI = (int)(255.0 / windowWith * (gray + (windowWith / 2) - windowCenter));
                        //grayGDI = (int)((gray - min) * 255.0 / (double)(max - min));

                        if (grayGDI > 255)
                            grayGDI = 255;
                        else if (grayGDI < 0)
                            grayGDI = 0;
                        c = Color.FromArgb(grayGDI, grayGDI, grayGDI);
                    }
                    else if (colors == 3)
                    {
                        c = Color.FromArgb(pixData[0], pixData[1], pixData[2]);
                    }

                    gdiImg.SetPixel(j, i, c);
                }
            }

            //MessageBox.Show(string.Format("max{0} min{1}", max, min));
            dicomFile.Close();
            return true;
        }

        private void tagRead()//不断读取所有tag 及其值 直到碰到图像数据 (7fe0 0010 )
        {
            bool enDir = false;
            int leve = 0;
            StringBuilder folderData = new StringBuilder();//该死的文件夹标签
            string folderTag = "";
            while (dicomFile.BaseStream.Position + 6 < dicomFile.BaseStream.Length)
            {
                //读取tag
                string tag = dicomFile.ReadUInt16().ToString("x4") + "," +
                dicomFile.ReadUInt16().ToString("x4");

                string VR = string.Empty;
                UInt32 Len = 0;
                //读取VR跟Len
                //对OB OW SQ 要做特殊处理 先置两个字节0 然后4字节值长度
                //------------------------------------------------------这些都是在读取VR一步被阻断的情况
                if (tag.Substring(0, 4) == "0002")//文件头 特殊情况
                {
                    VR = new string(dicomFile.ReadChars(2));

                    if (VR == "OB" || VR == "OW" || VR == "SQ" || VR == "OF" || VR == "UT" || VR == "UN")
                    {
                        dicomFile.BaseStream.Seek(2, SeekOrigin.Current);
                        Len = dicomFile.ReadUInt32();
                    }
                    else
                        Len = dicomFile.ReadUInt16();
                }
                else if (tag == "fffe,e000" || tag == "fffe,e00d" || tag == "fffe,e0dd")//文件夹标签
                {
                    VR = "**";
                    Len = dicomFile.ReadUInt32();
                }
                else if (isExplicitVR == true)//有无VR的情况
                {
                    VR = new string(dicomFile.ReadChars(2));

                    if (VR == "OB" || VR == "OW" || VR == "SQ" || VR == "OF" || VR == "UT" || VR == "UN")
                    {
                        dicomFile.BaseStream.Seek(2, SeekOrigin.Current);
                        Len = dicomFile.ReadUInt32();
                    }
                    else
                        Len = dicomFile.ReadUInt16();
                }
                else if (isExplicitVR == false)
                {
                    VR = getVR(tag);//无显示VR时根据tag一个一个去找 真tm烦啊。
                    Len = dicomFile.ReadUInt32();
                }
                //判断是否应该读取VF 以何种方式读取VF
                //-------------------------------------------------------这些都是在读取VF一步被阻断的情况
                byte[] VF = { 0x00 };

                if (tag == "7fe0,0010")//图像数据开始了
                {
                    pixDatalen = Len;
                    pixDataOffset = dicomFile.BaseStream.Position;
                    dicomFile.BaseStream.Seek(Len, SeekOrigin.Current);
                    VR = "UL";
                    VF = BitConverter.GetBytes(Len);
                }
                else if ((VR == "SQ" && Len == UInt32.MaxValue) || (tag == "fffe,e000" && Len == UInt32.MaxValue))//靠 遇到文件夹开始标签了
                {
                    if (enDir == false)
                    {
                        enDir = true;
                        folderData.Remove(0, folderData.Length);
                        folderTag = tag;
                    }
                    else
                    {
                        leve++;//VF不赋值
                    }
                }
                else if ((tag == "fffe,e00d" && Len == UInt32.MinValue) || (tag == "fffe,e0dd" && Len == UInt32.MinValue))//文件夹结束标签
                {
                    if (enDir == true)
                    {
                        enDir = false;
                    }
                    else
                    {
                        leve--;
                    }
                }
                else
                    VF = dicomFile.ReadBytes((int)Len);

                string VFStr;

                VFStr = getVF(VR, VF);

                //----------------------------------------------------------------针对特殊的tag的值的处理
                //特别针对文件头信息处理
                if (tag == "0002,0000")
                {
                    fileHeadLen = Len;
                    fileHeadOffset = dicomFile.BaseStream.Position;
                }
                else if (tag == "0002,0010")//传输语法 关系到后面的数据读取
                {
                    switch (VFStr)
                    {
                        case "1.2.840.10008.1.2.1\0"://显示little
                            isLitteEndian = true;
                            isExplicitVR = true;
                            break;
                        case "1.2.840.10008.1.2.2\0"://显示big
                            isLitteEndian = false;
                            isExplicitVR = true;
                            break;
                        case "1.2.840.10008.1.2\0"://隐式little
                            isLitteEndian = true;
                            isExplicitVR = false;
                            break;
                        default:
                            break;
                    }
                }
                for (int i = 1; i <= leve; i++)
                    tag = "--" + tag;
                //------------------------------------数据搜集代码
                if ((VR == "SQ" && Len == UInt32.MaxValue) || (tag == "fffe,e000" && Len == UInt32.MaxValue) || leve > 0)//文件夹标签代码
                {
                    folderData.AppendLine(tag + "(" + VR + ")：" + VFStr);
                }
                else if (((tag == "fffe,e00d" && Len == UInt32.MinValue) || (tag == "fffe,e0dd" && Len == UInt32.MinValue)) && leve == 0)//文件夹结束标签
                {
                    folderData.AppendLine(tag + "(" + VR + ")：" + VFStr);
                    tags.Add(folderTag + "SQ", folderData.ToString());
                }
                else
                    tags.Add(tag, "(" + VR + "):" + VFStr);
            }
        }

        private string getVR(string tag)
        {
            switch (tag)
            {
                case "0002,0000"://文件元信息长度
                    return "UL";
                case "0002,0010"://传输语法
                    return "UI";
                case "0002,0013"://文件生成程序的标题
                    return "SH";
                case "0008,0005"://文本编码
                    return "CS";
                case "0008,0008":
                    return "CS";
                case "0008,1032"://成像时间
                    return "SQ";
                case "0008,1111":
                    return "SQ";
                case "0008,0020"://检查日期
                    return "DA";
                case "0008,0060"://成像仪器
                    return "CS";
                case "0008,0070"://成像仪厂商
                    return "LO";
                case "0008,0080":
                    return "LO";
                case "0010,0010"://病人姓名
                    return "PN";
                case "0010,0020"://病人id
                    return "LO";
                case "0010,0030"://病人生日
                    return "DA";
                case "0018,0060"://电压
                    return "DS";
                case "0018,1030"://协议名
                    return "LO";
                case "0018,1151":
                    return "IS";
                case "0020,0010"://检查ID
                    return "SH";
                case "0020,0011"://序列
                    return "IS";
                case "0020,0012"://成像编号
                    return "IS";
                case "0020,0013"://影像编号
                    return "IS";
                case "0028,0002"://像素采样1为灰度3为彩色
                    return "US";
                case "0028,0004"://图像模式MONOCHROME2为灰度
                    return "CS";
                case "0028,0006"://颜色值排列顺序 可能骨头重建那个的显示就是这个问题
                    return "US";
                case "0028,0008"://图像的帧数
                    return "IS";
                case "0028,0010"://row高
                    return "US";
                case "0028,0011"://col宽
                    return "US";
                case "0028,0100"://单个采样数据长度
                    return "US";
                case "0028,0101"://实际长度
                    return "US";
                case "0028,0102"://采样最大值
                    return "US";
                case "0028,0103"://像素数据类型
                    return "US";
                case "0028,1050"://窗位
                    return "DS";
                case "0028,1051"://窗宽
                    return "DS";
                case "0028,1052":
                    return "DS";
                case "0028,1053":
                    return "DS";
                case "0040,0008"://文件夹标签
                    return "SQ";
                case "0040,0260"://文件夹标签
                    return "SQ";
                case "0040,0275"://文件夹标签
                    return "SQ";
                case "7fe0,0010"://像素数据开始处
                    return "OW";
                default:
                    return "UN";
            }
        }

        private string getVF(string VR, byte[] VF)
        {
            string VFStr = string.Empty;
            if (isLitteEndian == false)//如果是big字节序 先把数据翻转一下
                Array.Reverse(VF);
            switch (VR)
            {
                case "SS":
                    VFStr = BitConverter.ToInt16(VF, 0).ToString();
                    break;
                case "US":
                    VFStr = BitConverter.ToUInt16(VF, 0).ToString();

                    break;
                case "SL":
                    VFStr = BitConverter.ToInt32(VF, 0).ToString();

                    break;
                case "UL":
                    VFStr = BitConverter.ToUInt32(VF, 0).ToString();

                    break;
                case "AT":
                    VFStr = BitConverter.ToUInt16(VF, 0).ToString();

                    break;
                case "FL":
                    VFStr = BitConverter.ToSingle(VF, 0).ToString();

                    break;
                case "FD":
                    VFStr = BitConverter.ToDouble(VF, 0).ToString();

                    break;
                case "OB":
                    VFStr = BitConverter.ToString(VF, 0);
                    break;
                case "OW":
                    VFStr = BitConverter.ToString(VF, 0);
                    break;
                case "SQ":
                    VFStr = BitConverter.ToString(VF, 0);
                    break;
                case "OF":
                    VFStr = BitConverter.ToString(VF, 0);
                    break;
                case "UT":
                    VFStr = BitConverter.ToString(VF, 0);
                    break;
                case "UN":
                    VFStr = Encoding.Default.GetString(VF);
                    break;
                default:
                    VFStr = Encoding.Default.GetString(VF);
                    break;
            }
            return VFStr;
        }
    }
}